// Author: Bjorn Schobben

#include "Camera.h"
#include <stdio.h>

const Scalar Camera::DEFAULT_MOVESPEED = 1.0/4;
const Scalar Camera::DEFAULT_TURNSPEED = 10;
const Vector Camera::DEFAULT_EYEPOINT = Vector(1.01*1,1.01*1);
// Cameraplane and direction are unit vectors
const Vector Camera::DEFAULT_CAMERAPLANE = Vector(0, 0.66 * 1);
const Vector Camera::DEFAULT_DIRECTION = Vector(1 * 1, 0);


Camera::Camera(): world(0), cameraPlane(DEFAULT_CAMERAPLANE), direction(DEFAULT_DIRECTION), eyepoint(DEFAULT_EYEPOINT), turnSpeed(DEFAULT_TURNSPEED), moveSpeed(DEFAULT_MOVESPEED), angle(0)
{
}

Camera::~Camera()
{
}

// Speed to turn at
void Camera::SetTurnSpeed(const Scalar& newTurnSpeed)
{
	turnSpeed = newTurnSpeed;
}

// Speed to move at
void Camera::SetMoveSpeed(const Scalar& newMoveSpeed)
{
	moveSpeed = newMoveSpeed;
}



void Camera::TurnLeft()
{
	angle -= turnSpeed;

	RotatePlanes();
}

void Camera::TurnRight()
{
	angle += turnSpeed;

	RotatePlanes();
}

void Camera::TurnLeft(const Scalar& turnRatio)
{
	angle -= turnRatio * turnSpeed;

	RotatePlanes();
}

void Camera::TurnRight(const Scalar& turnRatio)
{
	angle += turnRatio * turnSpeed;

	RotatePlanes();
}

// Clamp angle into 0..360 range
void Camera::ClampAngle()
{
	if (angle < 0)
		angle += 360;
	if (angle > 360)
		angle -= 360;
}

void Camera::RotatePlanes()
{
	ClampAngle();

	// Rotate direction
	direction = DEFAULT_DIRECTION;
	direction.Rotate(angle);

	// Rotate camera plane
	cameraPlane = DEFAULT_CAMERAPLANE;
	cameraPlane.Rotate(angle);
}

void Camera::MoveForward()
{
	Vector test = eyepoint + direction * moveSpeed;

	CheckCollision(test);
}

void Camera::MoveBackward()
{
	Vector test = eyepoint - direction * moveSpeed;

	CheckCollision(test);
}

void Camera::MoveForward(const Scalar& moveRatio)
{
	Vector test = eyepoint + direction * moveSpeed * moveRatio;

	CheckCollision(test);
}

void Camera::MoveBackward(const Scalar& moveRatio)
{
	Vector test = eyepoint - direction * moveSpeed * moveRatio;

	CheckCollision(test);
}

void Camera::MoveLeft()
{
	Vector test = eyepoint - direction.GetNormal() * moveSpeed;

	CheckCollision(test);
}

void Camera::MoveRight()
{
	Vector test = eyepoint + direction.GetNormal() * moveSpeed;

	CheckCollision(test);
}

const Vector& Camera::GetEyepoint() const
{
	return eyepoint;
}

const Vector& Camera::GetDirection() const
{
	return direction;
}

const Vector& Camera::GetCameraPlane() const
{
	return cameraPlane;
}

void Camera::SetWorld(const World& newWorld)
{
	world = &newWorld;

	eyepoint = world->GetCameraPos();
	angle = world->GetCameraAngle();

	RotatePlanes();
}

void Camera::CheckCollision(Vector test)
{
	if (0 == world)
		return;

	int mapX = eyepoint.GetIntX();
	int mapY = eyepoint.GetIntY();
	int mapTestX = test.GetIntX();
	int mapTestY = test.GetIntY();

	// -0.1 gets converted into 0 integer, so we need to check that
	if (test.GetX().IsNegative())
		mapTestX = -1;
	if (test.GetY().IsNegative())
		mapTestY = -1;

	// Check if X position is good, ifso, allow this move
	if (mapTestX >= 0 && mapTestX < world->GetMapWidth() && world->IsEmptyCell(mapTestX, mapY))
	{
		eyepoint.SetX(test.GetX());
		mapX = eyepoint.GetIntX();
	}
	// Check if Y position is good, ifso, allow this move
	if (mapTestY >= 0 && mapTestY < world->GetMapHeight() && world->IsEmptyCell(mapX, mapTestY))
		eyepoint.SetY(test.GetY());
}

